<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>发布订阅</title>
  </head>
  <body>
    <p>
      发布订阅模式：订阅者关注（订阅）发布者，发布者将订阅者存在对象/数组中，当发布者发布消息时，会遍历对象通知订阅者
    </p>

    <button id="z-btn">张三订阅</button>
    <button id="l-btn">李四订阅</button>
    <button id="emit-btn">发布消息</button>
  </body>
  <script>
    let zBtn = document.querySelector("#z-btn");
    let lBtn = document.querySelector("#l-btn");
    let emitBtn = document.querySelector("#emit-btn");

    zBtn.onclick = function () {
      console.log("张三订阅了 event1 普通事件");
      eventProxy.on("event1", function (data) {
        console.log("张三订阅的 event1 事件发生了", data);
      });
    };

    lBtn.onclick = function () {
      console.log("李四订阅了 event2 一次性事件");
      eventProxy.one("event2", function (data) {
        console.log("李四订阅的 event2 事件发生了", data);
      });
    };

    emitBtn.onclick = function () {
      console.log("派发事件");
      eventProxy.trigger("event1", "event1 data");
      eventProxy.trigger("event2", "event2 data");
    };

    // 订阅事件代理对象
    const eventProxy = {
      // 存放普通订阅事件及对应事件函数
      onObj: {},
      // 存放仅触发一次的事件及对应函数
      oneObj: {},
      // 注册事件
      on: function (key, fn) {
        // 若注册事件不存在，初始化事件类型及函数存放数组
        if (this.onObj[key] === undefined) {
          this.onObj[key] = [];
        }
        // 函数存放至数组内
        return this.onObj[key].push(fn) - 1;
      },
      // 注册一次性事件
      one: function (key, fn) {
        if (this.oneObj[key] === undefined) {
          this.oneObj[key] = [];
        }
        return this.oneObj[key].push(fn) - 1;
      },
      // 取消事件注册，清空对应全部事件函数
      off: function (key, id) {
        if (typeof id === "number" && 0 <= id && id < this[key].length) {
          this.onObj[key][id] = undefined;
          this.oneObj[key][id] = undefined;
        } else {
          this.onObj[key] = [];
          this.oneObj[key] = [];
        }
      },
      // 触发自定义事件
      trigger: function () {
        let key, args;
        // 没有参数，不执行
        if (arguments.length == 0) {
          return false;
        }
        // 获取事件类型，及剩余参数
        key = arguments[0];
        args = [].concat(Array.prototype.slice.call(arguments, 1));

        // 触发普通事件函数队列
        if (this.onObj[key] !== undefined && this.onObj[key].length > 0) {
          for (let i in this.onObj[key]) {
            let fn = this.onObj[key][i];
            fn && fn.apply(null, args);
          }
        }
        // 触发一次性事件函数队列，并清空队列
        if (this.oneObj[key] !== undefined && this.oneObj[key].length > 0) {
          for (let i in this.oneObj[key]) {
            let fn = this.oneObj[key][i];
            fn && fn.apply(null, args);
          }
          delete this.oneObj[key];
        }
        console.log(this.onObj);
        console.log(this.oneObj);
      },
    };
  </script>
</html>
